#if !defined(FILE_SYSTEM_UTIL_H)
#define FILE_SYSTEM_UTIL_H
#include <fstream>
#include <vector>
#include <list>
#include <string>
using namespace std;
#include <Config.h>
#include <BasicTypes.h>
#include <StringUtil.h>
#include <DateTime.h>
#ifdef WIN32
#include <winbase.h>
#else

#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif

#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif

#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifndef INT_MAX
	#define INT_MAX 2147483647
#endif

#endif
namespace System
{
namespace IO
{
class FileAttributes
{
public:
	static const int ReadOnly			= 0x00000001;
	static const int Hidden				= 0x00000002;
	static const int System				= 0x00000004;
	static const int Directory			= 0x00000010;
	static const int Archive			= 0x00000020;
	static const int Device				= 0x00000040;
	static const int Normal				= 0x00000080;
	static const int Temporary			= 0x00000100;
	static const int SparseFile			= 0x00000200;
	static const int ReparsePoint		= 0x00000400;
	static const int Compressed			= 0x00000800;
	static const int Offline			= 0x00001000;
	static const int NotContentIndexed	= 0x00002000;
	static const int Encrypted			= 0x00004000;
public:
	static bool IsDirectory(int mode)
	{
		return (mode & Directory) == Directory;
	}
	static bool IsFile(int mode)
	{
		return !IsDirectory(mode);
	}
	static bool IsReadOnly(int mode)
	{
		return (mode & ReadOnly) == ReadOnly;
	}
};
class DirectoryInfo;

class FileInfo
{
public:
	FileInfo(const string& fileName);
	FileInfo(const FileInfo& fi)
	{
		this->operator=(fi);
	}
	FileInfo& operator=(const FileInfo& fi)
	{
		fileName = fi.fileName;
		return *this;
	}
public:		// properties
	u_int32_t GetAttributes();
	void SetAttributes(u_int32_t attributes);

	DirectoryInfo GetDirectory();
	string DirectoryName();
	bool Exists();
	string Extension();
	string FullName();
	bool IsReadOnly();
	int64_t Length();
	string Name();

	DateTime CreationTime();
	DateTime CreationTimeUtc();
	DateTime LastAccessTime();
	DateTime LastAccessTimeUtc();
	DateTime LastWriteTime();
	DateTime LastWriteTimeUtc();
public:		// methods
	bool Create();
	bool CopyTo(const string& destFileName, bool overwrite = false);
	bool MoveTo(const string& destFileName);
	bool Delete();
private:
	string fileName;
};

class FileSystemInfo
{
public:
	static DateTime GetCreationTime(const string& path);
	static DateTime GetCreationTimeUtc(const string& path);
	static DateTime GetLastAccessTime(const string& path);
	static DateTime GetLastAccessTimeUtc(const string& path);
	static DateTime GetLastWriteTime(const string& path);
	static DateTime GetLastWriteTimeUtc(const string& path);
	static bool IsDirectory(const string& path);
	static bool IsFile(const string& path);
protected:
#ifdef WIN32
	static bool GetPathInfo(const string& path, WIN32_FIND_DATA& wfd);
#else
	static bool GetPathInfo(const string& path, struct stat& info);
#endif
};

class File : public FileSystemInfo
{
public:
	static bool Copy(const string& fromPath, const string& toPath, bool overwrite = false);
	static bool Delete(const string& path);
	static bool Move(const string& sourceFileName, const string& destFileName);
	static bool Exists(const string& path);
	static bool CreateEmptyFile(const string& path);

	static u_int32_t GetAttributes(const string& path);
	static void SetAttributes(const string& path, u_int32_t attributes);
	static int64_t GetLength(const string& path);

	static bool ReadAllBytes(const string& path, string& bytes);
	static bool WriteAllBytes(const string& path, const string& bytes);
	static bool AppendAllBytes(const string& path, const string& bytes);
	static bool ReadAllBytes(const string& path, vector<u_int8_t>& bytes);
	static bool WriteAllBytes(const string& path, const vector<u_int8_t>& bytes);
	static bool AppendAllBytes(const string& path, const vector<u_int8_t>& bytes);
	static bool ReadAllLines(const string& path, vector<string>& lines);
	static bool WriteAllLines(const string& path, const vector<string>& lines);
	static bool AppendAllLines(const string& path, const vector<string>& lines);
	static bool ReadAllText(const string& path, string& text);
	static bool WriteAllText(const string& path, const string& text);
	static bool AppendAllText(const string& path, const string& text);
};

class DirectoryInfo
{
public:
	DirectoryInfo(const string& directory);
	DirectoryInfo(const DirectoryInfo& di)
	{
		this->operator=(di);
	}
	DirectoryInfo& operator=(const DirectoryInfo& di)
	{
		if(this == &di)
			return *this;
		directory = di.directory;
		return *this;
	}
	void LoadDirectory(const string& directory);
public:		// properties
	u_int32_t GetAttributes();

	bool GetParent(DirectoryInfo& directoryInfo);
	bool GetRoot(DirectoryInfo& root);
	bool Exists();
	string FullName();
	bool IsReadOnly();
	string Name();

	DateTime CreationTime();
	DateTime CreationTimeUtc();
	DateTime LastAccessTime();
	DateTime LastAccessTimeUtc();
	DateTime LastWriteTime();
	DateTime LastWriteTimeUtc();
public:
	bool Create(bool recursive = false);
	bool CreateSubDirectory(const string& path, bool recursive = false);
	bool CreateSubDirectory(const string& path, DirectoryInfo& directoryInfo, bool recursive =false);
	bool Delete(bool recursive = false);
	bool GetDirectories(vector<DirectoryInfo>& subDirectories);
	bool GetFiles(vector<FileInfo>& files);
	bool MoveTo(const string& dest);
	bool CopyTo(const string& destDir, bool overwrite = false);
private:
	string directory;
};

class Directory : public FileSystemInfo
{
public:
	static bool Create(const string& path, bool recursive = false);
	static bool Create(const string& path, DirectoryInfo& di, bool recursive = false);
	static bool Delete(const string& path, bool recursive = false);
	static bool Exists(const string& path);
	static string GetCurrentDirectory();
	static bool SetCurrentDirectory(const string& directory);
	static string GetDirectoryRoot(const string path);
	static bool GetDirectories(const string& path, vector<string>& directories);
	static bool GetFiles(const string& path, vector<string>& files);
	static bool GetFileSystemEntries(const string& path, vector<string>& entries);
	static bool GetLogicalDrives(vector<string>& drives);
	static bool GetParent(const string& path, DirectoryInfo& di);
	static bool Move(const string& sourceDirName, const string& destDirName);
	static bool Copy(const string& sourceDir, const string& destDir, bool overwrite = false);
};
class Path
{
public:
#ifdef WIN32
	static const char DirectorySeparatorChar = '\\';
	static const char AltDirectorySeparatorChar = '/';
#else
	static const char DirectorySeparatorChar = '/';
	static const char AltDirectorySeparatorChar = '/';
#endif
	static const string ValidDirectorySeparatorChars;
	static bool IsDirectorySeparatorChar(char c);
	static string ChangeExtension(const string& path, const string& extension);
	static string Combine(const string& base, const string& path);
	static string GetDirectoryName(const string& path);
	static string GetExtension(const string& path);
	static string GetFileName(const string& path);
	static string GetFileNameWithoutExtension(const string& path);
	static string GetFullPath(const string& path);
	static string GetPathRoot(const string& path);
	static string GetRandomFileName();
	static string GetTempFileName();
	static string GetTempPath();
	static bool HasExtension(const string& path);
	static bool IsPathRooted(const string& path);
};


/* Ŀ¼����
 * ����:����'/'��'\\'��ʼ��Ŀ¼���ɾ���Ŀ¼�����ַ�����Ϊ��Ŀ¼
 * ����Ŀ¼ֱ����Ŀ¼��ʼ
 * ��׼��ʽ��Ŀ¼��
 * �Ѿ�ȥ����ͷ��β�Ŀո�������\/��������DirectorySeparatorChar�����ұ�����DirectorySeparatorChar����
 * ��׼����·��A~Z:\...
 * ��׼����·������Ŀ¼\...\   
 * ��ǰĿ¼: .\   
 * DirectoryString��������¼Ŀ¼Ԫ�أ�����¼'.'��Ŀ¼������'.'�ͺ���֮����¼��Ϊ�ձ�ʾ'.'Ŀ¼
 * Ŀ¼�е�һ��
 */
class DirectoryStringItem
{
public:
	explicit DirectoryStringItem(const string& _strItem = "")
	{
		this->operator=(_strItem);
	}
	DirectoryStringItem(const DirectoryStringItem& di)
	{
		this->operator=(di);
	}
	DirectoryStringItem& operator=(const string& _strItem)
	{
		strItem = _strItem;
		trim(strItem);
		if(!strItem.empty() && Path::IsDirectorySeparatorChar(strItem[strItem.size() - 1]))
			strItem.resize(strItem.size() - 1);
		if(strItem.empty())
		{
			Type = DIT_Root;
		}
		else if(Path::IsDirectorySeparatorChar(strItem[0]))
		{
			strItem.resize(0);
			Type = DIT_Root;
		}
		else if(strItem.size() >= 2 && strItem[1] == ':')
		{
			strItem.resize(2);		// stores X:
			Type = DIT_Root;
		}
		else if(strItem.size() >= 2 && strItem[0] == '.' && strItem[1] == '.')
		{
			strItem.resize(0);
			Type = DIT_DotDot;
		}
		else if(strItem[0] == '.')
		{
			strItem.resize(0);
			Type = DIT_Dot;
		}
		else
		{
			Type = DIT_Normal;
		}
		return *this;
	}
	DirectoryStringItem& operator=(const DirectoryStringItem& di)
	{
		Type = di.Type;
		strItem = di.strItem;
		return *this;
	}
	bool operator==(const DirectoryStringItem& pi) const
	{
		if(Type != pi.Type)
			return false;
		switch(Type)
		{
		case DIT_Normal:
		case DIT_Root:
#ifdef WIN32
			if(strItem.size() != pi.strItem.size())
				return false;
			return strnicmp(strItem.c_str(), pi.strItem.c_str(), strItem.size()) == 0;
#else
			return strItem == pi.strItem;
#endif
		case DIT_Dot:
		case DIT_DotDot:
			return true;
		default:
			assert(false);
		}
		return false;
	}
	bool operator !=(const DirectoryStringItem& pi) const
	{
		return !this->operator ==(pi);
	}
	string ToString() const
	{
		switch(Type)
		{
		case DIT_Normal:
		case DIT_Root:
			return strItem + Path::DirectorySeparatorChar;
		case DIT_Dot:
			return string(".") + Path::DirectorySeparatorChar;
		case DIT_DotDot:
			return string("..") + Path::DirectorySeparatorChar;
		default:
			assert(false);
		}
		return "error";
	}
	string GetPureText() const
	{
		switch(Type)
		{
		case DIT_Normal:
		case DIT_Root:
			return strItem;
		case DIT_Dot:
			return ".";
		case DIT_DotDot:
			return "..";
		default:
			assert(false);
		}
		return "error";
	}
	int GetType() const
	{
		return Type;
	}
	static const DirectoryStringItem& Dot()
	{
		static const DirectoryStringItem dsi(".");
		return dsi;
	}
	static const DirectoryStringItem& DotDot()
	{
		static const DirectoryStringItem dsi("..");
		return dsi;
	}

public:
	static const int DIT_Normal = 0;			//��ͨ�ļ���
	static const int DIT_Root	= 1;			//�̷�
	static const int DIT_Dot	= 2;			//��ǰ·��
	static const int DIT_DotDot = 3;			//�ϼ�·��
private:
	int	   Type;
	string strItem;
};

// �յ�DirectoryString���ǿյ�·������Ĭ��Ϊĳ������·��
class DirectoryString
{	
public:
	DirectoryString(const string& strPath = "")
	{
		this->operator=(strPath);
	}
	DirectoryString(const DirectoryString& ds)
	{
		this->operator=(ds);
	}
	DirectoryString& operator=(const string& strPath)
	{
		items.clear();
		string temp = strPath;
		trim(temp);
		if(temp.empty())
			return *this;
		DirectoryStringItem di;
		string::size_type start_index = 0, end_index = temp.find_first_of(Path::ValidDirectorySeparatorChars);
		while(true)
		{
			if(end_index == string::npos)
				di = temp.substr(start_index);
			else
				di = temp.substr(start_index, end_index - start_index);
			if(items.empty() && di.GetType() == DirectoryStringItem::DIT_Root)
			{
				items.push_back(di);
			}
			else
				this->operator+=(di);
			if(end_index == string::npos)
				break;
			start_index = end_index + 1;
			end_index = temp.find_first_of(Path::ValidDirectorySeparatorChars, start_index);
		}
		return *this;
	}
	DirectoryString& operator=(const DirectoryString& ds)
	{
		items = ds.items;
		return *this;
	}
	DirectoryString& operator+=(const DirectoryStringItem& di)
	{
		int type = di.GetType();
		switch(type)
		{
		case DirectoryStringItem::DIT_Dot:
			if(items.empty())
				items.push_back(di);
			break;
		case DirectoryStringItem::DIT_Normal:
			//�������ߵ�DIT_Dot
			while(!items.empty())
			{
				if(items.back().GetType() == DirectoryStringItem::DIT_Dot)
					items.pop_back();
				else
					break;
			}
			items.push_back(di);
			break;
		case DirectoryStringItem::DIT_Root:
			if(items.empty())			//��Ŀ¼��ֻ�ܼ��ڵ�һ��
				items.push_back(di);
			break;
		case DirectoryStringItem::DIT_DotDot:
			while(!items.empty())
			{
				if(items.back().GetType() == DirectoryStringItem::DIT_Dot)
					items.pop_back();
				else
					break;
			}
			if(items.empty())
			{
				items.push_back(di);
			}
			else
			{
				if(items.back().GetType() == DirectoryStringItem::DIT_Normal)
				{
					items.pop_back();
				}
				else
				{
					items.push_back(di);
				}
			}
			break;
		default:
			assert(false);
		}
		return *this;
	}
	DirectoryString& operator+=(const DirectoryString& ds)
	{
		if(ds.IsRooted())
			return *this;
		for(list<DirectoryStringItem>::const_iterator it = ds.items.begin(); it != ds.items.end(); ++it)
		{
			this->operator+=(*it);
		}
		return *this;
	}
	DirectoryString operator+(const DirectoryStringItem& di) const
	{
		return DirectoryString(*this).operator+=(di);
	}
	DirectoryString operator+(const DirectoryString& ds) const
	{
		return DirectoryString(*this).operator+=(ds);
	}
	bool Empty() const
	{
		return items.empty();
	}
	void Clear()
	{
		items.clear();
	}
	bool IsRooted() const
	{
		if(items.empty())
			return false;
		return items.front().GetType() == DirectoryStringItem::DIT_Root;
	}
	bool IsRelative() const
	{
		return !IsRooted();
	}
	string ToString() const
	{
		if(items.empty())
			return "";
		ostringstream ostr;
		for(list<DirectoryStringItem>::const_iterator it = items.begin(); it != items.end(); ++it)
		{
			ostr << it->ToString();
		}
		return ostr.str();
	}
	int GetItemCount()
	{
		return (int)items.size();
	}
	DirectoryStringItem PopBack()
	{
		DirectoryStringItem fsi = items.back();
		items.pop_back();
		return fsi;
	}
	const DirectoryStringItem& Back()
	{
		return items.back();
	}
	bool GotoParent()
	{
		if(items.empty())
		{
			items.push_back(DirectoryStringItem::DotDot());
			return true;
		}
		switch(items.back().GetType())
		{
		case DirectoryStringItem::DIT_Normal:
			items.pop_back();
			return true;
		case DirectoryStringItem::DIT_Root:
			return false;
		case DirectoryStringItem::DIT_Dot:
			items.pop_back();
			items.push_back(DirectoryStringItem::DotDot());
			return true;
		case DirectoryStringItem::DIT_DotDot:
			items.push_back(DirectoryStringItem::DotDot());
			return true;
		default:
			assert(false);
			return false;
		}
	}
	list<DirectoryStringItem>& GetInternalItems()
	{
		return items;
	}
	void ReleaseInternalItems()
	{
		list<DirectoryStringItem> itemsback = items;
		items.resize(0);
		list<DirectoryStringItem>::const_iterator it = itemsback.begin();
		for(; it != itemsback.end(); ++it)
		{
			this->operator += (*it);
		}
	}
	//����������ds������·��������ֵ��ʾ�����Ƿ�������·��
	bool MakeRelativeTo(const DirectoryString& base)
	{
		if(base.Empty())
			return IsRelative();
		if(base.IsRooted() != this->IsRooted())
			return IsRelative();
		if(base.IsRooted() && base.items.back() != this->items.back())
			return false;
		list<DirectoryStringItem>::const_iterator it1 = this->items.begin(), it2 = base.items.begin();
		// ȥ����ͬ����ʼ����
		while(it1 != this->items.end() && it2 != base.items.end() && *it1 == *it2)
		{
			++it1; ++it2;
			this->items.pop_front();
		}
		// ��ͬ�Ĳ�������..����
		DirectoryStringItem fsiDotDot("..");
        for(; it2 != base.items.end(); ++it2)
		{
			this->items.push_front(fsiDotDot);
		}
		return true;
	}
private:
	list<DirectoryStringItem> items;
	friend class PathString;
};

class PathString
{
public:
	PathString(const string& path = "")
	{
		this->operator=(path);
	}
	PathString(const string& directory, const string& _file) : ds(directory), file(_file)
	{
	}
	PathString(const PathString& ps)
	{
		this->operator=(ps);
	}
	PathString& operator=(const string& _strPath)
	{
		string strPath = _strPath;
		trim(strPath);
		string::size_type index = strPath.find_last_of(Path::ValidDirectorySeparatorChars);
		if(index == string::npos)
		{
			ds.Clear();
			file = strPath;
			return *this;
		}
		ds = strPath.substr(0, index);
		file = strPath.substr(index + 1);
		return *this;
	}
	PathString& operator=(const PathString& ps)
	{
		ds = ps.ds;
		file = ps.file;
		return *this;
	}
	PathString& AddBaseDirectory(const DirectoryString& base)
	{
		assert(!ds.IsRooted());
		ds = base + ds;
		return *this;
	}
	string ToString() const
	{
		return ds.ToString() + file;
	}
	bool Empty() const
	{
		return ds.Empty() && file.empty();
	}
	bool IsRooted() const
	{
		return ds.IsRooted();
	}
	bool IsRelative() const
	{
		return ds.IsRelative();
	}
	DirectoryString& GetDirectoryString()
	{
		return ds;
	}
	string GetFileName()
	{
		return file;
	}
	//�õ������չ����·�����ļ���
	string GetFileNameWithoutExtension()
	{
		string::size_type idx = file.find_last_of('.');
		return file.substr(0, idx);
	}
	string GetExtension()
	{
		string::size_type indexDot = file.find_last_of('.');
		if(indexDot == string::npos)
			return "";
		return file.substr(indexDot);
	}
	bool MakeRelativeTo(const DirectoryString& base)
	{
		return ds.MakeRelativeTo(base);
	}
private:
	DirectoryString ds;			//Ŀ¼����
	string		 file;			//�ļ�����
};
inline ostream& operator<<(ostream& ostr, const PathString& ps)
{
	ostr << ps.ToString();
	return ostr;
}

inline PathString operator+(const DirectoryString& ds, const PathString& ps)
{
	PathString temp(ps);
	temp.AddBaseDirectory(ds);
	return temp;
}


// read big endian byte order integer from stream
template <typename ISTREAM, typename T>
inline bool ReadBinaryIntBE(ISTREAM& istr, T& value)
{
	istr.read((char*)&value, sizeof(value));
	FromBigEndian(value);
	return !istr.fail();
}

// write integer to stream in big endian byte order
template <typename OSTREAM, typename T>
inline bool WriteBinaryIntBE(OSTREAM& ostr, T value)
{
	ToBigEndian(value);
	ostr.write((char*)&value, sizeof(value));
	return !ostr.fail();
}

// read little endian byte order integer from stream
template <typename ISTREAM, typename T>
inline bool ReadBinaryIntLE(ISTREAM& istr, T& value)
{
	istr.read((char*)&value, sizeof(value));
	FromLittleEndian(value);
	return !istr.fail();
}

// write integer to stream in little endian byte order
template <typename OSTREAM, typename T>
inline bool WriteBinaryIntLE(OSTREAM& ostr, T value)
{
	ToLittleEndian(value);
	ostr.write((char*)&value, sizeof(value));
	return !ostr.fail();
}

template <typename ISTREAM, typename T>
inline bool ReadBinaryRaw(ISTREAM& istr, T& value)
{
	istr.read((char*)&value, sizeof(value));
	return !istr.fail();
}
template <typename OSTREAM, typename T>
inline bool WriteBinaryRaw(OSTREAM& ostr, const T& value)
{
	ostr.write((char*)&value, sizeof(value));
	return !ostr.fail();
}
template<typename ISTREAM>
inline bool ReadBinaryString(ISTREAM& istr, string& str, int32_t MinLen = 0, int32_t MaxLen = INT_MAX)
{
	int32_t temp = 0;
	if(!ReadBinaryIntBE(istr, temp))
		return false;
	if(!istr)
		return false;
	if(temp < MinLen || temp > MaxLen)
	{
		istr.setstate(ios::failbit);
		return false;
	}
	str.resize(temp);
	istr.read(&str[0], temp);
	if(!istr)
		return false;
	if(strlen(str.c_str()) != str.size())
	{
		istr.setstate(ios::failbit);
		return false;
	}
	return true;
}
template<typename OSTREAM>
inline bool WriteBinaryString(OSTREAM& ostr, const string& str)
{
	if(!WriteBinaryIntBE(ostr, (int32_t)str.size()))
		return false;
	ostr.write(str.c_str(), (std::streamsize)str.size());
	return !ostr.fail();
}
#ifdef WIN32


typedef int64_t streamoff64;

class streampos64
{
	typedef streampos64 _Myt;
	typedef _Mbstatet _Statetype;
public:
	streampos64(streamoff64 _Off = 0)
		: _Myoff(_Off), _Fpos(0), _Mystate(0)
		{	// construct with stream offset
		}

	streampos64(_Statetype _State, fpos_t _Fileposition)
		: _Myoff(0), _Fpos(_Fileposition), _Mystate(_State)
		{	// construct with conversion state and C file position
		}

	_Statetype state() const
		{	// return conversion state
		return (_Mystate);
		}

	void state(_Statetype _State)
		{	// set conversion state
		_Mystate = _State;
		}

	fpos_t seekpos() const
		{	// return C file position
		return (_Fpos);
		}

	operator streamoff64() const
		{	// return offset
		return (_Myoff + _Fpos);
		}

	streamoff64 operator-(const _Myt& _Right) const
		{	// return difference of file positions as an offset
		return ((streamoff64)*this - (streamoff64)_Right);
		}

	_Myt& operator+=(streamoff64 _Off)
		{	// add offset
		_Myoff += _Off;
		return (*this);
		}

	_Myt& operator-=(streamoff64 _Off)
		{	// subtract offset
		_Myoff -= _Off;
		return (*this);
		}

	_Myt operator+(streamoff64 _Off) const
		{	// return this + offset
		_Myt _Tmp = *this;
		return (_Tmp += _Off);
		}

	_Myt operator-(streamoff64 _Off) const
		{	// return this - offset
		_Myt _Tmp = *this;
		return (_Tmp -= _Off);
		}

	bool operator==(const _Myt& _Right) const
		{	// test for file position equality
		return ((streamoff64)*this == (streamoff64)_Right);
		}

	bool operator!=(const _Myt& _Right) const
		{	// test for file position inequality
		return (!(*this == _Right));
		}

private:
	streamoff64 _Myoff;	// stream offset
	fpos_t _Fpos;	// C file position
	_Statetype _Mystate;	// current conversion state
};
template<typename Elem>
class char_traits64 : public char_traits<Elem>
{
public:
	typedef Elem char_type;
	typedef long int_type;
	typedef streampos64 pos_type;
	typedef streamoff64 off_type;
	typedef _Mbstatet state_type;
};
typedef basic_ifstream<char, char_traits64<char> > ifstream64;
typedef basic_ofstream<char, char_traits64<char> > ofstream64;
typedef basic_fstream<char, char_traits64<char> > fstream64;
#else
typedef ifstream ifstream64;
typedef ofstream ofstream64;
typedef fstream fstream64;
#endif
}
}

#endif // !defined(FILE_SYSETM_UTIL_H)
